/******************************************************************************
 * SAGE - Scalable Adaptive Graphics Environment
 *
 * Module: sageSync.h
 * Author : Byungil Jeong, Rajvikram Singh, 
 * Description : This file includes classes to synchronize multiple groups
 *               of synchronization slaves in the SAGE processes and a class
 *               to synchronize multiple threads in a SAGE process.
 *
 * Copyright (C) 2004 Electronic Visualization Laboratory,
 * University of Illinois at Chicago
 *
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the distribution.
 *  * Neither the name of the University of Illinois at Chicago nor
 *    the names of its contributors may be used to endorse or promote
 *    products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Direct questions, comments etc about SAGE to bijeong@evl.uic.edu or 
 * http://www.evl.uic.edu/cavern/forum/
 *
 *****************************************************************************/

#ifndef _SAGESYNC_H
#define _SAGESYNC_H

#include "sageBase.h"

#define SAGE_SYNC_MSG_LEN  1280
#define MAX_SYNC_GROUP     100
#define SYNC_MSG_BUF_LEN   64

#define SAGE_UPDATE_SETUP    1
#define SAGE_UPDATE_FOLLOW   2
#define SAGE_UPDATE_FRAME    3
#define SAGE_UPDATE_AUDIO    4

#define SAGE_CONSTANT_SYNC   1
#define SAGE_ASAP_SYNC_HARD  2
#define SAGE_ASAP_SYNC_SOFT  3

#define NORMAL_SYNC          1
#define SKIP_FRAME           2


class syncSlaveData {
public:
   int            clientSockFd;
   sockaddr_in      clientAddr;
   int            frame;
   
   syncSlaveData() : frame(0) {}
};

//forward declarations
class sageSyncClient;
class sageSyncServer;

#define MAX_INTERVAL_ERROR 0.1
#define SYNC_TIMEOUT       1667   // 1sec/60 * 10% = 1667 us 

class syncGroup {
protected:
   int noOfUpdates, slaveNum;
   pthread_mutex_t   *syncMsgLock;
   std::deque<char *> syncMsgQueue;
   int id, policy, curFrame, audioFrame, videoFrame, keyFrame, skipFrame, audioSyncCnt;
   double interval;  // minimum sync signal interval
   double timeError; 
   bool syncEnd;
   sageSyncServer *syncServer;
   pthread_t threadID;
   sageTimer timer;
   pthread_mutex_t  syncLock;
   pthread_cond_t   resumeSync;
   bool holdSync;
   bool waitingInterval;
   bool waitForKeyFrame;
   
public:
   syncGroup() : id(0), noOfUpdates(0), curFrame(0), slaveNum(0), syncEnd(false), interval(0.0),
      policy(SAGE_ASAP_SYNC_SOFT), videoFrame(0), audioFrame(-1), keyFrame(100), holdSync(false),
      timeError(0.0), waitingInterval(false), waitForKeyFrame(false), skipFrame(0), audioSyncCnt(0)
   { syncMsgQueue.clear(); }
   
   int init(int startFrame, int p, int groupID, int frameRate = 1, int sNum = 1);
   int setFrameRate(float frameRate);
   bool checkInterval();
   int processUpdate(char *data);
   int enqueSyncMsg(char *msg);
   char* dequeSyncMsg();
   int getSyncID() { return id; }
   bool checkTimeOut();
   void blockSync();
   void unblockSync();
   void checkHold();
   inline bool isWaiting() { return waitingInterval; }
   
   friend class sageSyncServer;
   friend class sageSyncClient;
};

class sageSyncServer {
protected:
   int serverSockFd;
   int asapSyncGroupNum, maxSyncGroupID;
   int maxSlaveSockFd;
   bool syncEnd;
   
   sockaddr_in serverAddr;
   fd_set slaveFds;
   
   pthread_t   syncThreadID;
   std::vector<syncSlaveData> syncSlaves;
   syncGroup *syncGroupArray[MAX_SYNC_GROUP]; 
   
   static void* syncServerThread(void*);
   static void* managerThread(void*);
   int manageUpdate();
   int sendSync(syncGroup *grp, int cmd = NORMAL_SYNC);
   sageTimer timer;
         
public:
   sageSyncServer();
   ~sageSyncServer();
   
   int init(int port);
   int addSyncGroup(syncGroup *grp); 
   void killAllClients();  // closes all the open client sockets
   int checkTimeOut();
}; // End of sageSyncServer

class sageCircBufSingle;

class syncMsgStruct {
public:
   int frameID;
   char *data;
   
   syncMsgStruct() : frameID(0), data(NULL) {}
   syncMsgStruct(int size) : frameID(0) { data = new char[size]; }
   ~syncMsgStruct() { if (data) delete [] data; }
};

class sageSyncClient {
private:
   int clientSockFd;
   int maxGroupID;
   sageCircBufSingle *syncMsgBuf[MAX_SYNC_GROUP]; 
   bool syncEnd; 
   
   pthread_t   syncThreadID;
   static void* syncClientThread(void *args);
   int readSyncMsg();

public:
   sageSyncClient();
   int connectToServer(char*, int);
   int addSyncGroup(int id);
   int removeSyncGroup(int id);
   ~sageSyncClient();

   int sendSlaveUpdate(int frame, int id = 0, int rcvNum = 0, int type = SAGE_UPDATE_FOLLOW);
   int waitForSync(char* msg);
   syncMsgStruct* waitForSync(int id);
   int waitForSyncData(char* &data);
   int checkSync(char* &msg);
}; // End of tvSyncClient

// class for synchronization among master and slave threads
class sageThreadSync {
private:
   pthread_mutex_t   *slaveLock;
   pthread_mutex_t   *masterLock;
   int slaveNum;

public:
   sageThreadSync(int sNum = 1);
   ~sageThreadSync();
   int synchronize(int rank = 0);
};

#endif
